﻿//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: ID_1.cs
//
//  Description: ID class for a persistent object.
// 
//--------------------------------------------------------------------------

using System;
using System.Xml.Serialization;

namespace Chili.Opf3
{
	/// <summary>
	/// Class that represents an identifier in the storage.
	/// </summary>
	/// <remarks>
	/// This class can be used to represent an Identifier in the storage.
	/// It is not necessary to use this class as identifier.
	/// </remarks>
	/// <example>
	/// The following example shows how to use the class in a persistent object.
	///		<code lang="CS">
	/// [Persistent("USER")]
	/// public sealed class User
	/// {
	///		[Field("NAME", Mandatory = false)]
	///		public string Name
	///		{
	///			// ... Code of the property.
	///		}
	/// 
	///		[Field("ID", Mandatory = true, Identifier = true)]
	///		public ID&lt;string&gt; Id
	///		{
	///			// ... Code of the property.
	///		}
	/// 
	///		// ... Other code of the class.
	/// }
	/// 
	/// // ... Other code.
	/// 
	/// User user = new User();
	/// user.Id = ID.CreateGuidID();
	/// 
	/// User user1 = new User();
	/// user1.Id = ID.CreateGuidID();
	/// 
	/// // Checks the values of the both IDs.
	/// if (user.Id == user1.Id)
	/// {
	///		// Should not enter here.
	/// }
	/// 
	/// // Copy the ID from one object to the other.
	/// // Now both IDs are conntected, since they are
	/// // the same instance.
	/// user.Id = user1.Id;
	///		</code>
	/// </example>
	[Serializable]
	public sealed class ID<T> : IUserDefinedType, ICloneable where T: IComparable
	{
		private T _value = default(T);

		/// <summary>
		/// Creates a new instance of the <see cref="Opf3.ID{T}">ID Class.</see>
		/// </summary>
		public ID()
		{
		}

		/// <summary>
		/// Creates a new instance of the <see cref="Opf3.ID{T}">ID Class.</see>
		/// </summary>
		/// <param name="value">Value of the ID.</param>
		/// <exception cref="ArgumentNullException">Value is null.</exception>
		public ID(T value)
		{
			_value = value;
		}

		/// <summary>
		/// Specifies the current value of the ID.
		/// </summary>
		/// <value>
		/// The current value of the ID.
		/// </value>
		public T Value
		{
			get { return _value; }
			set { _value = value; }
		}

		/// <summary>
		/// Gets whether the ID has a value set.
		/// </summary>
		/// <value>
		/// Returns true if the ID has a value.
		/// </value>
		[XmlIgnore]
		public bool HasValue
		{
			get 
			{
				if (_value == null)
					return false;
				return _value.CompareTo(default(T)) != 0; 
			}
		}

		#region IUserDefinedType Members

		/// <summary>
		/// Populates the <see cref="Opf3.IUserDefinedType">IUserDefinedType</see> 
		/// with data from the storage.
		/// </summary>
		/// <param name="value">Value from the storage.</param>
		void IUserDefinedType.FromSimpleDataType(object value)
		{
			_value = (T)Convert.ChangeType(value, typeof(T));
		}

		/// <summary>
		/// Converts the <see cref="Opf3.IUserDefinedType">IUserDefinedType</see> to a type 
		/// that can be saved to the storage.
		/// </summary>
		/// <returns>Returns a value that represents the <see cref="Opf3.IUserDefinedType">IUserDefinedType</see>
		/// converted to a simple type.</returns>
		object IUserDefinedType.ToSimpleDataType()
		{
			return _value;
		}

		/// <summary>
		/// Returns the type supported by the class implementing this interface. The supported type is the type
		/// that is returned when converting to a simple type and the type that is set when converting from the simple type.
		/// </summary>
		Type IUserDefinedType.SupportedType
		{
			get { return typeof(T); }
		}

		#endregion

		#region ICloneable Members

		/// <summary>
		/// Creates a new object that is a copy of the current instance.
		/// </summary>
		/// <returns>A new object that is a copy of this instance.</returns>
		public object Clone()
		{
			ID<T> id = new ID<T>();
			id.Value = Value;
			return id;
		}

		#endregion

		/// <summary>
		/// Checks the values and returns true, if both have the same value.
		/// </summary>
		/// <param name="id1">First ID to check.</param>
		/// <param name="id2">Second ID to check.</param>
		/// <returns>Returns true if both IDs have the same value.</returns>
		public static bool operator ==(ID<T> id1, ID<T> id2)
		{
			// Check if one or both objects are null.
			if (Object.ReferenceEquals(id1, null) && Object.ReferenceEquals(id2, null))
				return true;
			if (Object.ReferenceEquals(id1, null) && !Object.ReferenceEquals(id2, null))
				return false;
			if (!Object.ReferenceEquals(id1, null) && Object.ReferenceEquals(id2, null))
				return false;

			return id1.Equals(id2);
		}

		/// <summary>
		/// Checks the values and returns true, if both have different values.
		/// </summary>
		/// <param name="id1">First ID to check.</param>
		/// <param name="id2">Second ID to check.</param>
		/// <returns>Returns true if both IDs have different values.</returns>
		public static bool operator !=(ID<T> id1, ID<T> id2)
		{
			return !(id1 == id2);
		}

		/// <summary>
		/// Determines whether two instances are equal.
		/// </summary>
		/// <param name="obj">Object that is checked to be equal with the current one.</param>
		/// <returns>Returns true if both objects are equal.</returns>
		public override bool Equals(object obj)
		{
			if (obj == null) 
				return false;
			if (this.GetType() != obj.GetType()) 
				return false;
			ID<T> id = (ID<T>)obj;

			if (Value == null && id.Value == null)
				return true;
			if (Value == null || id.Value == null)
				return false;

			return Value.CompareTo(id.Value) == 0;
		}

		/// <summary>
		/// Returns the hash code of the object.
		/// </summary>
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}
	}
}
